home *** CD-ROM | disk | FTP | other *** search
/ Software Vault: The Gold Collection / Software Vault - The Gold Collection (American Databankers) (1993).ISO / cdr10 / bagtag2.zip / CTAGS.C < prev    next >
C/C++ Source or Header  |  1993-06-19  |  38KB  |  1,215 lines

  1. /****************************************************************************
  2. *
  3. *   ctags.c                                             19Jun93
  4. *
  5. *   Generate tags for subsequent use by Brief editor.
  6. *
  7. *   Contains entries:
  8. *       main
  9. *       usage
  10. *       find_functions
  11. *       get_typedefs
  12. *       get_defines
  13. *       get_structs
  14. *       get_token
  15. *       print_defn_line
  16. *       not_c_keyword
  17. *
  18. *   Usage: ctags [flags] file(s) [[flags] file(s) ...] [>ctags.tag]
  19. *
  20. *   Flags (/ or - for flag character):
  21. *     -d    Include defines (default);  -!d excludes them
  22. *     -t    Include typedefs (default); -!t excludes them
  23. *     -s    Include structs (default);  -!s excludes them
  24. *     -u    Include unions (default);   -!u excludes them
  25. *     -e    Include enums (default);    -!e excludes them
  26. *     -     Signals end of flags
  27. *
  28. *   This program will perform a simple parsing of one or more C source
  29. *   files and write a "tags" file to stdout. This file is then used in
  30. *   conjunction with tagging commands build into VI and available (via
  31. *   the ctags.cb macro) for Brief. The tags file will contain a line for
  32. *   each procedure in the source file. Each line has the form:
  33. *
  34. *   <tag name> <file name> <search criteria>
  35. *
  36. *   The search criteria contains the entire source line containing the
  37. *   tag name to reduce the possibility of the search finding the
  38. *   wrong line.
  39. *
  40. *   This has been compiled under Microsoft C V6.0a (and used to compile
  41. *   under Unix V on an AT&T 3B1). When using Microsoft C, link with
  42. *   setargv.obj to enable wild card expansion of command line arguments.
  43. *
  44. *   To build: CL /AT ctags.c setargv.obj wilds.obj
  45. *    (wilds.obj replaces Microsoft's wild.c and does real reg-exp wildcard
  46. *        processing, including such things as "*d.*" and "e[a-f]*.[ch]")
  47. *
  48. *   For debug build, CL /AS /Zi /Od /DDEBUG ctags.c setargv.obj wilds.obj
  49. *
  50. *   Futures:
  51. *     >> Flags below were from the VI version; not implemented here
  52. *          -a   append output to existing `tags' file.
  53. *                (Silly, since output goes to stdout -- but see below)
  54. *
  55. *          -B   generate backward (from the current position) searches
  56. *          -F   generate forward searches (default)
  57. *                (Huh?)
  58. *
  59. *          -w   suppress warnings
  60. *                (What warnings?)
  61. *
  62. *          -x   produce a more human readable report on standard output
  63. *                (Including sorting?)
  64. *
  65. *     >> Look for duplicates?
  66. *
  67. *     >> How to organize a project's tag files? One big file or separate
  68. *       by directories? Beware of duplicate tags.
  69. *
  70. *     >> If -a above is to make sense, we should make it
  71. *               -a<filename> (or -a <filename>)
  72. *       instead of re-directing output. This would also allow for
  73. *               -u<filename> (or -u <filename>)
  74. *       meaning update the output file (i.e., remove all tag lines for the
  75. *       input file before checking it for tags).
  76. *       (And maybe change to (or allow) -f<filename> to supplant stdout.)
  77. *
  78. *     >> Allow for adding or suppressing specific tags.
  79. *
  80. *     >> Produce tags for variables (definitions of statics and external
  81. *       vars in particular).
  82. *
  83. *     >> Maybe use lex/yacc to do real parse of the program; might have to
  84. *       deal with such issues as pre-processor vs. final compiler scan;
  85. *       conditional compilation (#if family of statements); #includes;
  86. *       /D and /I parameters, INCLUDE environment var. But let's not go
  87. *       overboard.
  88. *
  89. *     >> Sky Schulz's SKYTAGS allows for duplicates by popping up a window 
  90. *       for user choice; but that only makes sense if all potential tag 
  91. *       files are read (he assumes one big tag file for a project, stored 
  92. *       centrally). If the one big file idea is adopted, save time by 
  93. *       reading it and saving it.
  94. *
  95. *   Copyright (c) 1992 B. Goldstein -- Pequod Software
  96. *
  97. *   Change History:
  98. *   Date    Who What
  99. *   ------- --- -------------
  100. *   01/11/87 Paul Verket Initial release
  101. *   02/01/87             Misc. minor enhancements.
  102. *   04/22/87             Tag #defined names in addition to function names.
  103. *   --------
  104. *   05/14/90 Michael Denio Modified to put the full path of the filename
  105. *                            in the tag file
  106. *   --------
  107. *   08/18/90 Morris Maynard Added cases to handle MSC dbl slash comments
  108. *   --------
  109. *   07/14/92 Edward Diener Modified to correctly handle an absolute or
  110. *                             relative file specification.
  111. *                          Eliminated C reserved keywords as possible
  112. *                             function names.
  113. *                          Added a pre-processor definition, NODEFINE,
  114. *                             that eliminates defined names being output.
  115. *                          Added recognition of new style // comments.
  116. *   --------
  117. *   21Oct92 BAG added code to print_defn_line to escape re chars in search
  118. *                   pattern
  119. *                 the following screws up:
  120. *                main F:\TMP\ctags.c ?<void main (int argc, char *argv[])>?
  121. *                 but this works:
  122. *                main F:\TMP\ctags.c ?<void main (int argc, char \*argv\[\])>?
  123. *                 (can't just turn RE off, since it's needed for < and >)
  124. *   22Oct92 BAG Changed generated pattern to use < and > for line brackets
  125. *   25Oct92 BAG Don't die if can't open a file (might be a directory, etc.,
  126. *                   especially since using setargv.obj)
  127. *   26Oct92 BAG Merged in changes from E.D. above
  128. *               Changed his NODEFINE to a run-time control:
  129. *                   -d: include #defines (default); -!d turns it off
  130. *               Allowed for #[ \t]@define with the "#" not in column 1
  131. *                   (note that this will accept #defines preceeded by
  132. *                   non-white-space on the line (in the real world, the
  133. *                   pre-processor scan is done as a separate pass).
  134. *   04Nov92 BAG line_start wasn't initialized, so if fn was on line 1, it
  135. *                   came up with an empty tag line (... ?<>?)
  136. *   05Nov92 BAG Added debug display of whole line
  137. *   11Nov92 BAG Added handling of typedefs
  138. *               Dropped pretense of checking for balanced parens or braces
  139. *   18Nov92 BAG Added handling of "[...]" in get_token, as T_SUBSCRIPT
  140. *               Improved typedef processing (skip first token, save word at
  141. *                   first close-paren, etc.)
  142. *   22Nov92 BAG Added handling of structs, unions, enums (including enum
  143. *               constants.
  144. *   22Nov92 BAG Added handling of things embedded within braces; this
  145. *               required dropping everything below the level of function
  146. *               tags into subroutines.
  147. *   22Nov92 BAG DebugBuffer seems to screw up file positioning; I think one
  148. *               can't mix fgets with fgetc's safely while doing fseeks and
  149. *               ftells.
  150. *   22Nov92 BAG Re-worked setting of line_start, so it won't be done early
  151. *               (it was set before the final word token was processed; now
  152. *               it's processed exactly when the T_NL token is returned).
  153. *   23Nov92 BAG Finishing "recursive" tagging
  154. *   25Nov92 BAG Allow for grouped flags: e.g.: -!sue
  155. *   19Jun93 BAG BUG: "struct foo bar;" screws up "parser"
  156. *                   ditto for enum, union
  157. *
  158. ****************************************************************************/
  159. /*  Definitions     */
  160.  
  161. #pragma comment (user,"Source file "__FILE__" modified on "__TIMESTAMP__)
  162. #pragma comment (user,"Compiled on "__DATE__" "__TIME__)
  163. #pragma comment (compiler)
  164.  
  165. #include <stddef.h>
  166. #include <stdio.h>
  167. #include <stdlib.h>
  168. #include <ctype.h>
  169. #include <string.h>
  170.  
  171. typedef enum
  172.  {  T_WORD, T_COMMA, T_SEMI,
  173.     T_PREPROCESS, T_TYPEDEF, T_STRUCT, T_UNION, T_ENUM,
  174.     T_OPENPAREN, T_CLOSEPAREN, T_OPENBRACE, T_CLOSEBRACE, T_NL,
  175.     T_SUBSCRIPT,
  176.     T_EOF
  177.  } TOKEN;
  178.  
  179. // Command line flag(s)
  180. static int WantDefines = 1;
  181. static int WantTypedefs = 1;
  182. static int WantStructs = 1;
  183. static int WantUnions = 1;
  184. static int WantEnums = 1;
  185. static int EndFlags = 0;
  186.  
  187. // Built-in limits
  188. #define MAX_TAGSIZE 132
  189.  
  190. // Here instead of as args
  191. static FILE *in_file;
  192. static char full_path[_MAX_PATH];
  193. static char word[MAX_TAGSIZE];
  194. static long line_start;
  195.  
  196. // Prototypes
  197. void main (int argc, char *argv[]);
  198. static void usage (void);
  199. static void find_functions (char *filename);
  200. static void get_typedefs (void);
  201. static void get_defines (void);
  202. static void get_structs (TOKEN t);
  203. static TOKEN get_token (void);
  204. static void print_defn_line (long defnstart);
  205. static int not_c_keyword (char *s);
  206.  
  207. /***************************************************************************/
  208.  
  209. /****************************************************************************
  210. *
  211. *   main                                                22Nov92
  212. *
  213. *   Scan arg list for flags and file names.
  214. *
  215. *   Change History:
  216. *   Date    Who What
  217. *   ------- --- -------------
  218. *   21Oct92 BAG Adopted
  219. *   22Nov92 BAG Changed scanning method; added s, u, e flags
  220. *
  221. ****************************************************************************/
  222.  
  223. void main (int argc, char *argv[])
  224.  
  225.  {  int argi;
  226.     int setting;
  227.     char *argp;
  228.  
  229.     if (argc < 2)
  230.         usage ();   // (it exits)
  231.  
  232.     /* Cycle through each argument on the command line. */
  233.  
  234.     for (argi = 1; argi < argc; argi++)
  235.      {  if (! EndFlags &&
  236.             (strcmp (argv[argi], "-") == 0 || strcmp (argv[argi], "/") == 0))
  237.          {  EndFlags = 1;
  238.             continue;
  239.          }
  240.  
  241.         if (argv[argi][0] == '-' && ! EndFlags
  242.                 || argv[argi][0] == '/')
  243.          {  EndFlags = 0;
  244.             setting = 1;    // default is On
  245.  
  246.             for (argp = &argv[argi][1]; *argp; argp++)
  247.              {  if (*argp == '!')
  248.                  {  setting = 0;
  249.                     continue; 
  250.                  }
  251.  
  252.                 switch (*argp)
  253.                  { case 'd':
  254.                    case 'D':
  255.                     WantDefines = setting;
  256.                     break;
  257.  
  258.                    case 't':
  259.                    case 'T':
  260.                     WantTypedefs = setting;
  261.                     break;
  262.  
  263.                    case 's':
  264.                    case 'S':
  265.                     WantStructs = setting;
  266.                     break;
  267.  
  268.                    case 'u':
  269.                    case 'U':
  270.                     WantUnions = setting;
  271.                     break;
  272.  
  273.                    case 'e':
  274.                    case 'E':
  275.                     WantEnums = setting;
  276.                     break;
  277.  
  278.                    default:
  279.                     usage ();
  280.                  }
  281.              }
  282.  
  283.             continue;
  284.          }
  285.  
  286.         in_file = fopen (argv[argi], "rt");
  287.         if (in_file == NULL)
  288.          {  perror (argv[argi]);
  289.             continue;   // Probably it was a directory
  290.          }
  291.         find_functions (argv[argi]);
  292.         fclose (in_file);
  293.      }
  294.  
  295.     exit (0);
  296.  }
  297.  
  298. /****************************************************************************
  299. *
  300. *   usage                                               25Nov92
  301. *
  302. *   Usage note and exit.
  303. *
  304. *   Change History:
  305. *   Date    Who What
  306. *   ------- --- -------------
  307. *   21Oct92 BAG Adopted
  308. *   25Nov92 BAG Explain about switch chars
  309. *
  310. ****************************************************************************/
  311.  
  312. static void usage (void)
  313.  
  314.  {  fprintf (stderr,
  315.       "\nUsage: CTAGS [flags] file(s) [[flags] file(s) ...] [>ctags.tag]\n");
  316.     fprintf (stderr, "   flags (signalled by - or /):\n");
  317.     fprintf (stderr, "       -d: include defines\n");
  318.     fprintf (stderr, "       -t: include typedefs\n");
  319.     fprintf (stderr, "       -s: include structs\n");
  320.     fprintf (stderr, "       -u: include unions\n");
  321.     fprintf (stderr, "       -e: include enums\n");
  322.     fprintf (stderr, "    ! turns off any following flags in the group\n\n");
  323.     fprintf (stderr, "        -: no more flags (until next /x flag)\n\n");
  324.     fprintf (stderr, "    Default flags: all on: -dtsue\n");
  325.  
  326.     exit (1);
  327.  }
  328.  
  329. /****************************************************************************
  330. *
  331. *   find_functions                                      19Jun93
  332. *
  333. *   Top level scan:
  334. *       Looks for function tags here
  335. *       Calls appropriate subroutine if it encounters another level to tag
  336. *
  337. *   Change History:
  338. *   Date    Who What
  339. *   ------- --- -------------
  340. *   21Oct92 BAG Adopted
  341. *   22Nov92 BAG Broke it up into subroutines so can get tags within {}
  342. *   24Nov92 BAG Look for other tag types within function body
  343. *   19Jun93 BAG "struct foo xxx;" bug fix
  344. *
  345. ****************************************************************************/
  346.  
  347. static void find_functions (char *filename)
  348.  
  349.  {  TOKEN curr_token;
  350.  
  351.     enum
  352.      {  NEUTRAL,
  353.         NAME, INPAREN, FN_NAME, INBRACE
  354.      } state = NEUTRAL;
  355.  
  356.     char function[MAX_TAGSIZE];
  357.  
  358.     int paren_cnt = 0,
  359.         brace_cnt = 0;
  360.  
  361.     long fn_start;
  362.  
  363.     // Get full path name of file; report it
  364.     if (_fullpath (full_path, filename, sizeof(full_path)-1) == NULL)
  365.         strcpy (full_path, filename);  // Can't happen: file's already open
  366.     fprintf (stderr, "Tagging file %s\n", full_path);
  367.  
  368.  
  369.     // Initialize
  370.     line_start = ftell (in_file);
  371.     fn_start = line_start;   // (Not really needed, but shuts LINT up)
  372.  
  373.  
  374.     // Process the file ...
  375.  
  376.     while ((curr_token = get_token ()) != T_EOF)
  377.      {  switch ((int) state)
  378.          {  // STARTING OUTSIDE ---------------------------------------------
  379.  
  380.             /* The "home" state. If a "word" is found, assume that it is
  381.                a procedure name. If the word is "typedef", scan for the
  382.                new type. If T_PREPROCESS, look for #define names and
  383.                toss the rest of the line since macro definitions look
  384.                like procedures. If an open brace is found, start gobbling
  385.                up the text contained within the braces. Keep a brace count
  386.                to handle nested braces. */
  387.  
  388.            case NEUTRAL:
  389.             switch ((int)curr_token)
  390.              { case T_WORD:
  391.                 state = NAME;
  392.                 // Note that the parens may start on the next line,
  393.                 //  so store the offset now
  394.                 fn_start = line_start;
  395.                 continue;
  396.  
  397.                case T_PREPROCESS:
  398.                 get_defines ();
  399.                 continue;
  400.  
  401.                case T_TYPEDEF:
  402.                 get_typedefs ();
  403.                 continue;
  404.  
  405.                case T_STRUCT:
  406.                case T_UNION:
  407.                case T_ENUM:
  408.                 get_structs (curr_token);
  409.                 continue;
  410.  
  411.                case T_OPENBRACE:
  412.                 state = INBRACE;
  413.                 brace_cnt = 1;
  414.                 continue;
  415.  
  416.                default:
  417.                 continue;
  418.              }
  419.  
  420.  
  421.             // FUNCTION TAG -------------------------------------------------
  422.  
  423.             /* All subsequent "word"s will be assumed to be the real
  424.                function name until an open paren is found. If something
  425.                other than a word or paren is found, then this wasn't
  426.                a function name after all. */
  427.  
  428.             // Ah, but! the word might have been "static" or "_FAR_" and
  429.             //  we really can't just skip all words up to the one before
  430.             //  the open paren, for we may have hit "struct" or its sisters
  431.  
  432.            case NAME:
  433.             switch ((int)curr_token)
  434.              { case T_WORD:
  435.                 fn_start = line_start;
  436.                 continue;
  437.  
  438.                case T_STRUCT:
  439.                case T_UNION:
  440.                case T_ENUM:
  441.                 get_structs (curr_token);
  442.                 continue;
  443.  
  444.                case T_NL:
  445.                 continue;
  446.  
  447.                case T_OPENPAREN:
  448.                 state = INPAREN;
  449.                 strcpy (function, word);
  450.                 paren_cnt = 1;
  451.                 continue;
  452.  
  453.                default:
  454.                 state = NEUTRAL;
  455.                 continue;
  456.              }
  457.  
  458.             /* Eat up all the stuff within parens until the close paren
  459.                is found. Keep a counter to handle nested parens. */
  460.  
  461.            case INPAREN:
  462.             switch ((int)curr_token)
  463.              { case T_OPENPAREN:
  464.                 paren_cnt++;
  465.                 continue;
  466.  
  467.                case T_CLOSEPAREN:
  468.                 if (--paren_cnt == 0)
  469.                     state = FN_NAME;
  470.                 continue;
  471.  
  472.                default:
  473.                 continue;
  474.              }
  475.  
  476.             /* If a comma or a semicolon is found, then this was a false
  477.                alarm. If an opening brace or another word is found, then
  478.                we found a procedure definition. */
  479.  
  480.            case FN_NAME:
  481.             switch ((int)curr_token)
  482.              { case T_COMMA:
  483.                case T_SEMI:
  484.                 state = NEUTRAL;
  485.                 continue;
  486.  
  487.                case T_NL:
  488.                 continue;
  489.  
  490.                case T_OPENBRACE:
  491.                 state = INBRACE;
  492.                 brace_cnt = 1;
  493.                 if (not_c_keyword (function))
  494.                  {  printf ("%s", function);
  495.                     print_defn_line (fn_start);
  496.                  }
  497.                 continue;
  498.  
  499.                default:
  500.                 state = NEUTRAL;
  501.                 if (not_c_keyword (function))
  502.                  {  printf ("%s", function);
  503.                     print_defn_line (fn_start);
  504.                  }
  505.                 continue;
  506.              }
  507.  
  508.             // Loop through function body until the closing brace is found.
  509.             //  Here is where to alter things to find defines,
  510.             //   typedefs, etc., within functions.
  511.  
  512.            case INBRACE:
  513.             switch ((int)curr_token)
  514.              { case T_OPENBRACE:
  515.                 brace_cnt++;
  516.                 continue;
  517.  
  518.                case T_PREPROCESS:
  519.                 get_defines ();
  520.                 continue;
  521.  
  522.                case T_TYPEDEF:
  523.                 get_typedefs ();
  524.                 continue;
  525.  
  526.                case T_STRUCT:
  527.                case T_UNION:
  528.                case T_ENUM:
  529.                 get_structs (curr_token);
  530.                 continue;
  531.  
  532.                case T_CLOSEBRACE:
  533.                 if (--brace_cnt == 0)
  534.                     state = NEUTRAL;
  535.                 continue;
  536.  
  537.                default:
  538.                 continue;
  539.              }
  540.  
  541.            default:
  542.             continue;
  543.          }
  544.      }
  545.  
  546.     return;
  547.  }
  548.  
  549. /****************************************************************************
  550. *
  551. *   get_typedefs                                        24Nov92
  552. *
  553. *   Scan for typedefs
  554. *
  555. *   Skip all subsequent words and anything in () or {}, until
  556. *    we get a word followed by a comma or a semi-colon; such
  557. *    words are typedef tags. We want to save the line with the
  558. *    tag, since it will be a more distinctive search pattern
  559. *    than the "typedef" line itself.
  560. *
  561. *   This logic won't handle
  562. *     typedef void (_cdecl *PHANDLER)(void);
  563. *   so try skipping the first token ("void") and
  564. *    if there hasn't been a word at the first close-paren,
  565. *    use the current word (which should get "PHANDLER").
  566. *
  567. *   Change History:
  568. *   Date    Who What
  569. *   ------- --- -------------
  570. *   22Nov92 BAG First draft
  571. *   24Nov92 BAG Look for other tag types within the body of the typedef {}'s
  572. *
  573. ****************************************************************************/
  574.  
  575. void get_typedefs (void)
  576.  
  577.  {  TOKEN curr_token;
  578.  
  579.     enum
  580.      {  TYPEDEF0, TYPEDEF, TYPEDEFINPAREN, TYPEDEFINBRACE
  581.      } state = TYPEDEF0;
  582.  
  583.     char typedeftag[MAX_TAGSIZE];
  584.  
  585.     int paren_cnt = 0,
  586.         brace_cnt = 0;
  587.  
  588.     long typedef_start;
  589.  
  590.     typedeftag[0] = '\0';
  591.     word[0] = '\0';
  592.  
  593.     // The typedef last line is the desired search line, but we'll
  594.     //  store the current line offset here to start with
  595.     typedef_start = line_start;
  596.  
  597.     while ((curr_token = get_token ()) != T_EOF)
  598.      {  switch (state)
  599.          { case TYPEDEF0:
  600.             state = TYPEDEF;
  601.             switch ((int)curr_token)
  602.              { case T_STRUCT:
  603.                case T_UNION:
  604.                case T_ENUM:
  605.                 get_structs (curr_token);
  606.                 continue;
  607.  
  608.                default:
  609.                 continue; 
  610.              }
  611.  
  612.            case TYPEDEF:
  613.             switch ((int)curr_token)
  614.              { case T_WORD:
  615.                 strcpy (typedeftag, word);
  616.                 typedef_start = line_start;
  617.                 continue;
  618.  
  619.                case T_NL:
  620.                 continue;
  621.  
  622.                case T_OPENPAREN:
  623.                 state = TYPEDEFINPAREN;
  624.                 paren_cnt = 1;
  625.                 continue;
  626.  
  627.                case T_OPENBRACE:
  628.                 state = TYPEDEFINBRACE;
  629.                 brace_cnt = 1;
  630.                 continue;
  631.  
  632.                case T_COMMA:
  633.                case T_SEMI:
  634.                 if (WantTypedefs && *typedeftag)
  635.                  {  // have to check because of typedef void (*FOO)(int x);
  636.                     if (not_c_keyword (typedeftag))
  637.                      {  printf ("%s", typedeftag);
  638.                         print_defn_line (typedef_start);
  639.                      }
  640.                  }
  641.                 if (curr_token == T_SEMI)
  642.                     return;
  643.                 continue;
  644.  
  645.                default:
  646.                 continue;
  647.              }
  648.  
  649.            case TYPEDEFINPAREN:
  650.             switch ((int)curr_token)
  651.              { case T_OPENPAREN:
  652.                 paren_cnt++;
  653.                 continue;
  654.  
  655.                case T_CLOSEPAREN:
  656.                 if (--paren_cnt == 0)
  657.                  {  state = TYPEDEF;
  658.                     if (*typedeftag == '\0')
  659.                         strcpy (typedeftag, word);
  660.                  }
  661.                 continue;
  662.  
  663.                default:
  664.                 continue;
  665.              }
  666.  
  667.             // This section is what scans through typedef structs, so
  668.             //  here is where to alter things to find defines, structs,
  669.             //  unions, enums (typedefs are illegal)
  670.  
  671.            case TYPEDEFINBRACE:
  672.             switch ((int)curr_token)
  673.              { case T_OPENBRACE:
  674.                 brace_cnt++;
  675.                 continue;
  676.  
  677.                case T_PREPROCESS:
  678.                 get_defines ();
  679.                 continue;
  680.  
  681.                case T_STRUCT:
  682.                case T_UNION:
  683.                case T_ENUM:
  684.                 get_structs (curr_token);
  685.                 continue;
  686.  
  687.                case T_CLOSEBRACE:
  688.                 if (--brace_cnt == 0)
  689.                     state = TYPEDEF;
  690.                 continue;
  691.  
  692.                default:
  693.                 continue;
  694.              }
  695.  
  696.          }
  697.      }
  698.  
  699.     return;
  700.  }
  701.  
  702. /****************************************************************************
  703. *
  704. *   get_defines                                         22Nov92
  705. *
  706. *   Scan for defines
  707. *
  708. *   Change History:
  709. *   Date    Who What
  710. *   ------- --- -------------
  711. *   22Nov92 BAG First draft
  712. *
  713. ****************************************************************************/
  714.  
  715. void get_defines (void)
  716.  
  717.  {  TOKEN curr_token;
  718.  
  719.     enum
  720.      {  CHECK_DEFINE, RECORD_DEFINE, PREPROCESSOR
  721.      } state = CHECK_DEFINE;
  722.  
  723.     long define_start;
  724.  
  725.     define_start = line_start;
  726.  
  727.     while ((curr_token = get_token ()) != T_EOF && curr_token != T_NL)
  728.      {  switch (state)
  729.          { case CHECK_DEFINE:
  730.             switch ((int)curr_token)
  731.              { case T_WORD:
  732.                 if (strcmp (word, "define") == 0)
  733.                     state = RECORD_DEFINE;
  734.                 else
  735.                     state = PREPROCESSOR;
  736.                 continue;
  737.                default:
  738.                 state = PREPROCESSOR;
  739.                 continue;
  740.              }
  741.  
  742.             /* Record the defined name in the same way as function names */
  743.  
  744.            case RECORD_DEFINE:
  745.             if (WantDefines)
  746.              {  printf ("%s", word);
  747.                 print_defn_line (define_start);
  748.              }
  749.             state = PREPROCESSOR;       /* toss the rest */
  750.             continue;
  751.  
  752.             /* Handle the preprocessor line until a new-line is found.
  753.                The tokenizer tosses escaped new lines. */
  754.  
  755.            case PREPROCESSOR:
  756.             continue;    // skip everything to new line (better not hit eof)
  757.  
  758.            default:
  759.             continue;
  760.          }
  761.      }
  762.  
  763.     return;
  764.  }
  765.  
  766. /****************************************************************************
  767. *
  768. *   get_structs                                         19Jun93
  769. *
  770. *   Scan for structs, unions, enums (and enum constants)
  771. *
  772. *   Change History:
  773. *   Date    Who What
  774. *   ------- --- -------------
  775. *   22Nov92 BAG First draft
  776. *   24Nov92 BAG Look for tags within the body of this item
  777. *   24Nov92 BAG Process enum constants
  778. *   19Jun93 BAG BUG: "struct foo xxx;" with no {} screwed up
  779. *
  780. ****************************************************************************/
  781.  
  782. void get_structs (TOKEN token)
  783.  
  784.  {  TOKEN curr_token;
  785.  
  786.     enum
  787.      {  STRUCT0, STRUCT1, INBRACE, GOTENUMCONST, GOTTAG
  788.      } state = STRUCT0;
  789.  
  790.     int brace_cnt = 0;
  791.  
  792.     long struct_start;
  793.  
  794.     struct_start = line_start;
  795.  
  796.     while ((curr_token = get_token ()) != T_EOF)
  797.      {  switch (state)
  798.          { case STRUCT0:
  799.             switch ((int)curr_token)
  800.              { case T_WORD:     // if there's a word after "struct", take it
  801.                 struct_start = line_start;  // Remember this line
  802.                 state = GOTTAG;
  803.                 continue; 
  804.  
  805.                case T_NL:
  806.                 continue; 
  807.  
  808.                case T_OPENBRACE:
  809.                 brace_cnt = 1;
  810.                 state = INBRACE;
  811.                 continue; 
  812.  
  813.                default:
  814.                 state = STRUCT1;
  815.                 continue;
  816.              }
  817.  
  818.            case GOTTAG:         // Got tag; is it followed by word or '{'?
  819.             switch ((int)curr_token)
  820.              { case T_NL:
  821.                 continue; 
  822.  
  823.                case T_OPENBRACE:    // It really is a definition
  824.                 if (WantStructs || WantUnions || WantEnums)
  825.                  {  printf ("%s", word);
  826.                     print_defn_line (struct_start);
  827.                  }
  828.                 brace_cnt = 1;
  829.                 state = INBRACE;
  830.                 continue;
  831.  
  832.                default:             // it's just "struct foo xxx;"; skip it
  833.                 state = STRUCT1;
  834.                 continue;
  835.              }
  836.  
  837.            case STRUCT1:        // no tag: skip everything except braces, ';'
  838.             switch ((int)curr_token)
  839.              { case T_OPENBRACE:
  840.                 brace_cnt = 1;
  841.                 state = INBRACE;
  842.                 continue; 
  843.  
  844.                case T_SEMI:
  845.                 return;
  846.  
  847.                default:
  848.                 continue; 
  849.              }
  850.             
  851.             /* Here is where we would look for enum constants and also
  852.                 for nested structs, unions, enums, and for that matter
  853.                 embedded defines, structs, etc. (not typedefs). */
  854.  
  855.            case INBRACE:
  856.             switch ((int)curr_token)
  857.              { case T_OPENBRACE:
  858.                 brace_cnt++;
  859.                 continue;
  860.  
  861.                case T_PREPROCESS:
  862.                 get_defines ();
  863.                 continue;
  864.  
  865.                case T_STRUCT:
  866.                case T_UNION:
  867.                case T_ENUM:
  868.                 if (token != T_ENUM)
  869.                     get_structs (curr_token);
  870.                 continue;
  871.  
  872.                case T_CLOSEBRACE:
  873.                 if (--brace_cnt == 0)
  874.                     return;
  875.                 continue;
  876.  
  877.                 // look for enum constants within the braces
  878.                 // Note that if these are one to a line, the search
  879.                 //  pattern will hardly be unique
  880.                case T_WORD:
  881.                 if (token == T_ENUM)
  882.                  {  struct_start = line_start;
  883.                     if (WantEnums)
  884.                      {  printf ("%s", word);
  885.                         print_defn_line (struct_start);
  886.                      }
  887.                     state = GOTENUMCONST;
  888.                  }
  889.                 continue;
  890.                 
  891.                default:
  892.                 continue;
  893.              }
  894.  
  895.             // We only get here from INBRACE in an ENUM
  896.  
  897.            case GOTENUMCONST:   // skip everything to a comma or }
  898.             switch ((int)curr_token)
  899.              { case T_COMMA:
  900.                 state = INBRACE;    // go back to brace scanning
  901.                 continue; 
  902.  
  903.                case T_CLOSEBRACE:   // this ends the enum, so get out
  904.                 return;
  905.  
  906.                default:
  907.                 continue; 
  908.              }
  909.  
  910.            default:
  911.             continue;
  912.          }
  913.      }
  914.  
  915.     return;
  916.  }
  917.  
  918. /****************************************************************************
  919. *
  920. *   get_token                                           23Nov92
  921. *
  922. *       Break up input file into tokens. Take care with characters inside
  923. *   quotes and comments that might cause trouble.
  924. *
  925. *   Change History:
  926. *   Date    Who What
  927. *   ------- --- -------------
  928. *   21Oct92 BAG Adopted
  929. *
  930. ****************************************************************************/
  931.  
  932. static TOKEN get_token (void)
  933.  
  934.  {  enum
  935.      {  NEUTRAL, INQUOTE, INSQUOTE, INWORD, INCOMMENT, INDSLASH, INSUBSCRIPT
  936.      } state = NEUTRAL;
  937.     int c, c2;
  938.     char *w;
  939.  
  940.     w = word;
  941.  
  942.     while ((c = getc (in_file)) != EOF)
  943.      {  switch ((int) state)
  944.          {  /* The "home" state. Quoted strings and comments are
  945.                stripped. Words consisting of letters, digits and
  946.                the underscore are gathered. */
  947.  
  948.            case NEUTRAL:
  949.             switch (c)
  950.              { case '(':
  951.                 return (T_OPENPAREN);
  952.  
  953.                case ')':
  954.                 return (T_CLOSEPAREN);
  955.  
  956.                case '#':
  957.                 // Note that most compilers require the "#" to be the first
  958.                 //  non-white-space character on a line; some require it to
  959.                 //  be the first character on the line; I don't care so much
  960.                 return (T_PREPROCESS);
  961.  
  962.                case '\n':   // get start of what will be the next line
  963.                 line_start = ftell (in_file);
  964.                 return (T_NL);
  965.  
  966.                case '"':
  967.                 state = INQUOTE;
  968.                 continue;
  969.  
  970.                case '\'':
  971.                 state = INSQUOTE;
  972.                 continue;
  973.  
  974.                case '{':
  975.                 return (T_OPENBRACE);
  976.  
  977.                case '}':
  978.                 return (T_CLOSEBRACE);
  979.  
  980.                case '/':           /* start of comment? */
  981.                 if ((c2 = getc (in_file)) == '*')
  982.                  {  state = INCOMMENT;
  983.                     continue;
  984.                  }
  985.                 else if (c2 == '/')      /* double slash comment? */
  986.                  {  state = INDSLASH;
  987.                     continue;
  988.                  }
  989.                 else
  990.                  {  (void)ungetc (c2, in_file);
  991.                     continue;
  992.                  }
  993.  
  994.                case ';':
  995.                 return (T_SEMI);
  996.  
  997.                case ',':
  998.                 return (T_COMMA);
  999.  
  1000.                case '\\':          /* toss the escape */
  1001.                 (void)getc (in_file); // ignore char
  1002.                 continue;
  1003.  
  1004.                case '[':
  1005.                 state = INSUBSCRIPT;
  1006.                 continue;
  1007.  
  1008.                default:
  1009.                 if (isalnum (c) || c == '_')
  1010.                  {  state = INWORD;
  1011.                     //@@@ could check for overflow of MAX_TAGSIZE
  1012.                     *w++ = (char) c;
  1013.                  }
  1014.                 continue;
  1015.              }
  1016.  
  1017.  
  1018.             /* Stay in these states, tossing characters, until the
  1019.                closing marker. */
  1020.  
  1021.            case INCOMMENT:
  1022.             switch (c)
  1023.              { case '*':           /* end of comment? */
  1024.                 if ((c2 = getc (in_file)) == '/')
  1025.                     state = NEUTRAL;
  1026.                 else
  1027.                     (void)ungetc (c2, in_file);
  1028.                 continue;
  1029.                default:
  1030.                 continue;
  1031.              }
  1032.  
  1033.            case INDSLASH:
  1034.             switch (c)
  1035.              { case '\n':          /* end of //-comment? */
  1036.                 state = NEUTRAL;
  1037.                 (void)ungetc (c, in_file); // let top of loop see newline
  1038.                 continue;
  1039.                default:
  1040.                 continue;
  1041.              }
  1042.  
  1043.            case INQUOTE:
  1044.             switch (c)
  1045.              { case '"':
  1046.                 state = NEUTRAL;
  1047.                 continue;
  1048.                case '\\':          /* toss the escape */
  1049.                 (void)getc (in_file); // ignore char
  1050.                 continue;
  1051.                default:
  1052.                 continue;
  1053.              }
  1054.  
  1055.            case INSQUOTE:
  1056.             switch (c)
  1057.              { case '\'':
  1058.                 state = NEUTRAL;
  1059.                 continue;
  1060.                case '\\':          /* toss the escape */
  1061.                 (void)getc (in_file); // ignore char
  1062.                 continue;
  1063.                default:
  1064.                 continue;
  1065.              }
  1066.  
  1067.            case INSUBSCRIPT:
  1068.             //@@@ could check for overflow of MAX_TAGSIZE
  1069.             if (c == ']')
  1070.              {  *w = '\0';
  1071.                 return (T_SUBSCRIPT);
  1072.              }
  1073.             else
  1074.              {  *w++ = (char)c;
  1075.                 continue;
  1076.              }
  1077.  
  1078.  
  1079.             /* Gather up the word. */
  1080.  
  1081.            case INWORD:
  1082.             //@@@ could check for overflow of MAX_TAGSIZE
  1083.             if (isalnum (c) || c == '_')
  1084.              {  *w++ = (char)c;
  1085.                 continue;
  1086.              }
  1087.             else
  1088.              {  (void)ungetc (c, in_file);
  1089.                 *w = '\0';
  1090.                 if (strcmp (word, "typedef") == 0)
  1091.                     return (T_TYPEDEF);
  1092.                 else if (strcmp (word, "struct") == 0)
  1093.                     return (T_STRUCT);
  1094.                 else if (strcmp (word, "union") == 0)
  1095.                     return (T_UNION);
  1096.                 else if (strcmp (word, "enum") == 0)
  1097.                     return (T_ENUM);
  1098.                 else
  1099.                     return (T_WORD);
  1100.              }
  1101.  
  1102.            default:
  1103.             continue;
  1104.          }
  1105.      }
  1106.  
  1107.     return (T_EOF);
  1108.  }
  1109.  
  1110. /****************************************************************************
  1111. *
  1112. *   print_defn_line                                     22Nov92
  1113. *
  1114. *   Use the previously stored ftell() of the start of line to
  1115. *   dump the source line.
  1116. *
  1117. *   Escape any Regular Expression characters for Brief search
  1118. *
  1119. *   Change History:
  1120. *   Date    Who What
  1121. *   ------- --- -------------
  1122. *   21Oct92 BAG Adopted
  1123. *
  1124. ****************************************************************************/
  1125.  
  1126. static void print_defn_line (long defn_start)
  1127.  
  1128.  {  long current_position;
  1129.     int c;
  1130.  
  1131.     current_position = ftell (in_file);
  1132.     fseek (in_file, defn_start, SEEK_SET);
  1133.  
  1134.     printf (" %s ?<", full_path);
  1135.     while ((c = getc (in_file)) != EOF && c != '\n')
  1136.      {  // if RE-char, have to escape it
  1137.         if (strchr ("?*@+\\|%${}[]<>", c) != NULL)
  1138.             putchar ('\\');
  1139.         putchar (c);
  1140.      }
  1141.     printf (">?\n");
  1142.  
  1143.     fseek (in_file, current_position, SEEK_SET);
  1144.  
  1145.     return;
  1146.  }
  1147.  
  1148. /****************************************************************************
  1149. *
  1150. *   not_c_keyword                                       22Nov92
  1151. *
  1152. *   Check for C keywords (shouldn't find these looking like functions
  1153. *   outside of procedure bodies, but why not check?)
  1154. *
  1155. *   If this is actually useful, should be expandable (like GNU indent or
  1156. *     lint) to contain user-defined keywords.
  1157. *
  1158. *   Change History:
  1159. *   Date    Who What
  1160. *   ------- --- -------------
  1161. *   21Oct92 BAG Adopted
  1162. *
  1163. ****************************************************************************/
  1164.  
  1165. static int not_c_keyword (char *s)
  1166.  
  1167.  {  static char *CKeywords[] =
  1168.      {  "auto",
  1169.         "break",
  1170.         "case",
  1171.         "char",
  1172.         "const",
  1173.         "continue",
  1174.         "default",
  1175.         "do",
  1176.         "double",
  1177.         "else",
  1178.         "enum",
  1179.         "extern",
  1180.         "float",
  1181.         "for",
  1182.         "goto",
  1183.         "if",
  1184.         "int",
  1185.         "long",
  1186.         "register",
  1187.         "return",
  1188.         "short",
  1189.         "signed",
  1190.         "sizeof",
  1191.         "static",
  1192.         "struct",
  1193.         "switch",
  1194.         "typedef",
  1195.         "union",
  1196.         "unsigned",
  1197.         "void",
  1198.         "volatile",
  1199.         "while",
  1200.         NULL
  1201.      };
  1202.     int i;
  1203.  
  1204.     for (i = 0; CKeywords[i] != NULL; ++i)
  1205.         if (strcmp (s, CKeywords[i]) == 0)
  1206.          {  fprintf (stderr,
  1207.                           " Found C keyword %s outside of procedures\n", s);
  1208.             return (0);
  1209.          }
  1210.  
  1211.     return (1);
  1212.  }
  1213.  
  1214. /************************ end of ctags.c file ******************************/
  1215.